// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include <string>

#include "common/thread-debug-info.h"
#include "testutil/gtest-util.h"
#include "util/container-util.h"
#include "util/debug-util.h"
#include "util/thread.h"

#include "common/names.h"

namespace impala {

TEST(ThreadDebugInfo, Ids) {
  // This test checks if SetInstanceId() stores the
  // string representation of a TUniqueId correctly.
  ThreadDebugInfo thread_debug_info;
  TUniqueId instance_id;
  instance_id.hi = 123;
  instance_id.lo = 456;
  thread_debug_info.SetInstanceId(instance_id);
  string instance_id_str = PrintId(instance_id);

  EXPECT_EQ(instance_id, thread_debug_info.GetInstanceId());
  EXPECT_EQ(instance_id_str, PrintId(thread_debug_info.GetInstanceId()));

  TUniqueId query_id;
  query_id.hi = 1234;
  query_id.lo = 4567;
  thread_debug_info.SetQueryId(query_id);
  string query_id_str = PrintId(query_id);

  EXPECT_EQ(query_id, thread_debug_info.GetQueryId());
  EXPECT_EQ(query_id_str, PrintId(thread_debug_info.GetQueryId()));
}

TEST(ThreadDebugInfo, ThreadName) {
  // Checks if we can store the thread name.
  // If thread name is too long, the prefix of the thread name is stored.
  ThreadDebugInfo thread_debug_info;
  string thread_name = "thread-1";
  thread_debug_info.SetThreadName(thread_name);

  EXPECT_EQ(thread_name, thread_debug_info.GetThreadName());

  string a_255(255, 'a');
  string b_255(255, 'b');
  string a_b = a_255 + b_255;
  thread_debug_info.SetThreadName(a_b);
  string stored_name = thread_debug_info.GetThreadName();

  // Let's check if we stored the corrects parts of thread name
  string expected = a_255.substr(0, 244) + "..." + b_255.substr(0, 8);
  EXPECT_EQ(expected, stored_name);
}

TEST(ThreadDebugInfo, Global) {
  // Checks if the constructor of the local ThreadDebugInfo object set the
  // global pointer to itself.
  ThreadDebugInfo thread_debug_info;
  ThreadDebugInfo* global_thread_debug_info = GetThreadDebugInfo();

  EXPECT_EQ(&thread_debug_info, global_thread_debug_info);
}

TEST(ThreadDebugInfo, ThreadCreateRelationships) {
  // Checks if child thread extracts debug info from parent automatically.
  // Child's thread name is given in Thread::Create
  // Child's instance_id_ should be the same as parent's instance_id_
  // Child should store a copy of its parent's thread name.
  // Child should store its parent's system thread id.
  string parent_name = "Parent";
  string child_name = "Child";

  ThreadDebugInfo parent_tdi;
  parent_tdi.SetThreadName(parent_name);
  TUniqueId instance_id;
  instance_id.hi = 123;
  instance_id.lo = 456;
  parent_tdi.SetInstanceId(instance_id);

  TUniqueId query_id;
  query_id.hi = 123;
  query_id.lo = 456;
  parent_tdi.SetQueryId(query_id);

  std::unique_ptr<Thread> child_thread;
  auto f = [instance_id, query_id, child_name, parent_name, &parent_tdi]() {
    // In child's thread the global ThreadDebugInfo object points to the child's own
    // ThreadDebugInfo object which was automatically created in Thread::SuperviseThread
    ThreadDebugInfo* child_tdi = GetThreadDebugInfo();
    EXPECT_EQ(child_name, child_tdi->GetThreadName());
    EXPECT_EQ(instance_id, child_tdi->GetInstanceId());
    EXPECT_EQ(query_id, child_tdi->GetQueryId());
    EXPECT_EQ(parent_name, child_tdi->GetParentThreadName());
    EXPECT_EQ(parent_tdi.GetSystemThreadId(), child_tdi->GetParentSystemThreadId());
  };
  ASSERT_OK(Thread::Create("Test", child_name, f, &child_thread));
  child_thread->Join();
}

TEST(ThreadDebugInfo, Scoping) {
  TUniqueId id;
  id.hi = 123;
  id.lo = 456;
  TUniqueId id2;
  id.hi = 234;
  id.lo = 345;

  ThreadDebugInfo tdi;
  ASSERT_EQ(ThreadDebugInfo::ZERO_THREAD_ID, tdi.GetQueryId());
  ASSERT_EQ(ThreadDebugInfo::ZERO_THREAD_ID, tdi.GetInstanceId());
  {
    ScopedThreadContext s(&tdi, id);
    ASSERT_EQ(id, tdi.GetQueryId());
    ASSERT_EQ(ThreadDebugInfo::ZERO_THREAD_ID, tdi.GetInstanceId());
  }
  ASSERT_EQ(ThreadDebugInfo::ZERO_THREAD_ID, tdi.GetQueryId());
  ASSERT_EQ(ThreadDebugInfo::ZERO_THREAD_ID, tdi.GetInstanceId());
  {
    ScopedThreadContext s(&tdi, id, id2);
    ASSERT_EQ(id, tdi.GetQueryId());
    ASSERT_EQ(id2, tdi.GetInstanceId());
  }
  ASSERT_EQ(ThreadDebugInfo::ZERO_THREAD_ID, tdi.GetQueryId());
  ASSERT_EQ(ThreadDebugInfo::ZERO_THREAD_ID, tdi.GetInstanceId());
}

}
